const { Transform } = require('stream');
const cheerio = require('cheerio');
const superagent = require('superagent');
const charset = require('superagent-charset');
const temme = require("temme").default;
const request = charset(superagent);
const moment = require('moment');
const _ = require('lodash');
const utils = require('../../utils');

function formatSelector(items) {
  const group = items.group;
  delete items.group;
  const list = [];
  for (const key in items) {
    const item = items[key];
    const el = item[0] || '&';
    const prop = item[1];
    list.push(prop ? `${el}[${prop}=$${key}];` : `${el}{$${key}};`);
  }
  return group ? `${group}@{${list.join('')}}` : list.join('');
}
function parseHTML(html, selector) {
  let list;
  if (_.isFunction(selector)) {
    const $ = cheerio.load(html);
    list = selector($, { _, moment, utils });
  } else if (_.isObject(selector)) {
    list = temme(html, formatSelector(selector));
  } else {
    list = temme(html, selector);
  }
  return { list };
}
function parse(url, selector, encoding) {
  return new Promise(resolve => {
    request.get(url).charset(encoding).end((err, res) => {
      if (err) {
        console.error(url, err.message);
        return resolve({ error: true });
      }
      resolve(parseHTML(res.text, selector));
    });
  });
}

//
module.exports = function ({ encoding = 'utf-8', inFormat, outFormat } = {}) {
  const stream = new Transform({ objectMode: true });
  inFormat = utils.parseFunction(inFormat);
  outFormat = utils.parseFunction(outFormat);
  const errorList = [];

  stream._transform = async function (chunk, _encoding, callback) {
    utils.debug('htmlTransform [chunk]:', chunk);
    const params = inFormat ? await inFormat(chunk, { _, moment, utils }) : chunk;
    if (!params.html && !params.url) {
      throw new Error('acticleTransform必须有html或url');
    }
    let r = params.html ? parseHTML(params.html, params.selector) : await parse(params.url, params.selector, encoding);
    if (r.error) {
      errorList.push(chunk);
      callback();
      return;
    }
    let list = r.list;
    outFormat && (list = outFormat(list, { _, moment, utils, $$: chunk }));
    if (!_.isArray(list)) {
      stream.push(list);
      callback();
    } else {
      for (const obj of list) {
        stream.push(obj);
      }
      callback();
    }
  };
  stream.on('finish', function () {
    errorList.length && console.error('htmlTransform [error]:', errorList);
    utils.debug('htmlTransform [finish]');
  });

  return stream;
};
